package com.immediatus.engine.text;

import com.immediatus.engine.shape.RectangularShape;
import com.immediatus.graphics.buffer.TextTextureBuffer;
import com.immediatus.graphics.camera.Camera;
import com.immediatus.graphics.font.Font;
import com.immediatus.graphics.util.OpenGLWrapper;
import com.immediatus.graphics.vertex.TextVertexBuffer;
import com.immediatus.options.Align;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;


public class TextLabel extends RectangularShape{
    private final TextTextureBuffer _textTextureBuffer;

    private String _text;
    private String[] _lines;
    private int[] _widths;

    private final Font _font;

    private int _maximumLineWidth;

    private final int _charactersMaximum;
    private final int _vertexCount;


    public TextLabel(final float x_, final float y_, final Font font_, final String text_){
        this(x_, y_, font_, text_, Align.LEFT);
    }

    public TextLabel(final float x_, final float y_, final Font font_, final String text_, final Align align_){
        this(x_, y_, font_, text_, align_, text_.length() - countOccurrences(text_, '\n'));
    }

    protected TextLabel(final float x_, final float y_, final Font font_, final String text_, final Align align_, final int charactersMaximum_){
        super(x_, y_, 0, 0, new TextVertexBuffer(charactersMaximum_, align_, GL11.GL_STATIC_DRAW, true));

        this._charactersMaximum = charactersMaximum_;
        this._vertexCount = TextVertexBuffer.VERTICES_PER_CHARACTER * this._charactersMaximum;

        this._textTextureBuffer = new TextTextureBuffer(2 * this._vertexCount, GL11.GL_STATIC_DRAW, true);
        this._font = font_;

        this.updateText(text_);

        this.initBlendFunction();
    }

    protected void updateText(final String text_){
        this._text = text_;
        final Font font = this._font;

        this._lines = split(this._text, '\n', this._lines);
        final String[] lines = this._lines;

        final int lineCount = lines.length;
        final boolean widthsReusable = this._widths != null && this._widths.length == lineCount;
        if (!widthsReusable){
            this._widths = new int[lineCount];
        }
        final int[] widths = this._widths;

        int maximumLineWidth = 0;

        for (int i = lineCount - 1; i >= 0; i--){
            widths[i] = font.getStringWidth(lines[i]);
            maximumLineWidth = Math.max(maximumLineWidth, widths[i]);
        }
        this._maximumLineWidth = maximumLineWidth;

        super.setWidth(this._maximumLineWidth);
        final float width = super.getWidth();
        super.setBaseWidth(width);

        super.setHeight(lineCount * font.getLineHeight() + (lineCount - 1) * font.getLineGap());
        final float height = super.getHeight();
        super.setBaseHeight(height);

        final float centerX =  width * 0.5f;
        final float centerY =  height * 0.5f;

        this.setRotationCenterX(centerX);
        this.setRotationCenterY(centerY);

        this.setScaleCenterX(centerX);
        this.setScaleCenterY(centerY);

        this._textTextureBuffer.update(font, lines);
        this.updateVertexBuffer();
    }

    public String getText(){
        return this._text;
    }

    public int getCharactersMaximum(){
        return this._charactersMaximum;
    }

    @Override
    public TextVertexBuffer getVertexBuffer(){
        return (TextVertexBuffer) this._VertexBuffer;
    }

    @Override
    protected void onInitDraw(final GL10 gl_){
        super.onInitDraw(gl_);
        OpenGLWrapper.enableTextures(gl_);
        OpenGLWrapper.enableTexCoordArray(gl_);
    }

    @Override
    protected void drawVertices(final GL10 gl_, final Camera camera_){
        gl_.glDrawArrays(GL10.GL_TRIANGLES, 0, this._vertexCount);
    }

    @Override
    protected void onUpdateVertexBuffer(){
        final Font font = this._font;
        if (font != null){
            this.getVertexBuffer().update(font, this._maximumLineWidth, this._widths, this._lines);
        }
    }

    @Override
    protected void onApplyTransformations(final GL10 gl_){
        super.onApplyTransformations(gl_);
        this.applyTexture(gl_);
    }

    @Override
    protected void finalize() throws Throwable{
        super.finalize();
        if (this._textTextureBuffer.isManaged()){
            this._textTextureBuffer.unloadFromActiveBufferObjectManager();
        }
    }

    private void initBlendFunction(){
        if (this._font.getTexture().getTextureOptions()._PreMultipyAlpha){
            this.setBlendFunction(BLENDFUNCTION_SOURCE_PREMULTIPLYALPHA_DEFAULT, BLENDFUNCTION_DESTINATION_PREMULTIPLYALPHA_DEFAULT);
        }
    }

    private void applyTexture(final GL10 gl_){
        if (OpenGLWrapper.EXTENSIONS_VERTEXBUFFEROBJECTS){
            final GL11 gl11 = (GL11) gl_;

            this._textTextureBuffer.selectOnHardware(gl11);

            this._font.getTexture().bind(gl_);
            OpenGLWrapper.texCoordZeroPointer(gl11);
        } else{
            this._font.getTexture().bind(gl_);
            OpenGLWrapper.texCoordPointer(gl_, this._textTextureBuffer.getFloatBuffer());
        }
    }

    protected static String padFront(final String str_, final char c_, final int length_){
        final int padCount = length_ - str_.length();
        if (padCount <= 0){
            return str_;
        } else{
            final StringBuilder sb = new StringBuilder();

            for (int i = padCount - 1; i >= 0; i--){
                sb.append(c_);
            }
            sb.append(str_);

            return sb.toString();
        }
    }

    protected static int countOccurrences(final String str_, final char c_){
        int count = 0;

        int lastOccurrence = str_.indexOf(c_, 0);

        while (lastOccurrence != -1){
            count++;
            lastOccurrence = str_.indexOf(c_, lastOccurrence + 1);
        }

        return count;
    }

    protected static String[] split(final String str_, final char c_){
        return split(str_, c_, null);
    }

    protected static String[] split(final String str_, final char c_, final String[] reuse_){
        final int partCount = countOccurrences(str_, c_) + 1;

        final boolean reuseable = reuse_ != null && reuse_.length == partCount;
        final String[] out = (reuseable) ? reuse_ : new String[partCount];

        if (partCount == 0){
            out[0] = str_;
        } else{
            int from = 0;
            int to;

            for (int i = 0; i < partCount - 1; i++){
                to = str_.indexOf(c_, from);
                out[i] = str_.substring(from, to);
                from = to + 1;
            }

            out[partCount - 1] = str_.substring(from, str_.length());
        }

        return out;
    }
}